keras 模型部署

您所在的位置:网站首页 tensorflow gpu cpu pb keras 模型部署

keras 模型部署

#keras 模型部署| 来源: 网络整理| 查看: 265

今天笔者想记录一下深度学习模型的部署,不知道大家研究过没有,tensorflow模型有三种保存方式:

训练时我们会一般会将模型保存成:checkpoint文件 为了方便python,C++或者其他语言部署你的模型,你可以将模型保存成一个既包含网络结构又包含权重参数的:PB文件 为了方便使用TensorFlow Serving 部署你的模型,你可以将模型保存成:Saved_model文件

笔者是keras(tensorflow )的深度用户,经常把模型保存成HDF5格式。那么问题来了,如何把keras的模型转化成PB文件 或者 Saved_model文件供生成部署使用。今天笔者就是来介绍一下如何将Keras的模型保存成PB文件 或者 Saved_model文件。

定义BERT二分类模型

下方函数定义的是一个标准的BERT做文本二分类的图结构。

from keras.models import Model from keras.layers import * from keras import backend as K import tensorflow as tf from keras_bert import get_model,compile_model def load_bert_model_weight(bert_model_path): b_model = get_model(token_num=21128,) compile_model(b_model) bert_model = Model( inputs = b_model.input[:2], outputs = b_model.get_layer('Encoder-12-FeedForward-Norm').output ) x1_in = Input(shape=(None,)) x2_in = Input(shape=(None,)) x = bert_model([x1_in, x2_in]) x = Lambda(lambda x: x[:, 0])(x)## 取[CLS]向量 p = Dense(2, activation='softmax')(x) model = Model([x1_in, x2_in], p) # model.compile( # loss='binary_crossentropy', # optimizer=Adam(1e-5), # 用足够小的学习率 # metrics=['accuracy'] # ) model.load_weights(bert_model_path) return model model_path = "/opt/developer/wp/wzcq/model/bert1014v1_weights.hf" model = load_bert_model_weight(model_path)

具体结构如下图所示其中

model_2 就是含12层的transformer的bert模型, 之后接一个keras中经常使用的Lambda层用于抽取 bert最后一层出来的[CLS]位置对应特征向量, 将[CLS]的特征向量输入给一个全连接层做而分类。

由于笔者之前已经训练好了一个人模型,这里我直接使用load_bert_model_weight函数将模型以及模型参数加载进内存。

BERT二分类 将keras模型转化为PB文件

接下来可以使用这个函数将上市的model连模型图结构代参数一起保存下来,然后通过tensoflow 为python,java,c++语言等提供的模型调用接口使用起来了。

def freeze_session(session, keep_var_names=None, output_names=None, clear_devices=True): """ Freezes the state of a session into a pruned computation graph. Creates a new computation graph where variable nodes are replaced by constants taking their current value in the session. The new graph will be pruned so subgraphs that are not necessary to compute the requested outputs are removed. @param session The TensorFlow session to be frozen. @param keep_var_names A list of variable names that should not be frozen, or None to freeze all the variables in the graph. @param output_names Names of the relevant graph outputs. @param clear_devices Remove the device directives from the graph for better portability. @return The frozen graph definition. """ graph = session.graph with graph.as_default(): freeze_var_names = list(set(v.op.name for v in tf.global_variables()).difference(keep_var_names or [])) output_names = output_names or [] output_names += [v.op.name for v in tf.global_variables()] input_graph_def = graph.as_graph_def() if clear_devices: for node in input_graph_def.node: node.device = "" frozen_graph = tf.graph_util.convert_variables_to_constants( session, input_graph_def, output_names, freeze_var_names) return frozen_graph def save_keras_model_to_pb(keras_model , dic_pb=None, file_pb=None): """ save keras model to tf *.pb file """ frozen_graph = freeze_session(K.get_session(),output_names=[out.op.name for out in keras_model.outputs]) tf.train.write_graph(frozen_graph, dic_pb, file_pb, as_text=False) return save_keras_model_to_pb(model,"py","bert_model.pb") 将keras模型转化为Saved_model文件

而接下来的部分是如何将model制作成Saved_model文件,这样你就可以使用TensorFlow Serving 部署你的模型。这里笔者介绍一下使用TensorFlow Serving 部署你的模型的一些优势。

支持模型版本控制和回滚 支持并发,实现高吞吐量 开箱即用,并且可定制化 支持多模型服务 支持批处理 支持热更新 支持分布式模型 易于使用的inference api:为gRPC expose port 8500,为REST API expose port 8501 import tensorflow as tf import os from tensorflow.keras.losses import categorical_crossentropy from tensorflow.keras.optimizers import Adadelta def export_model(model, export_model_dir, model_version ): """ :param export_model_dir: type string, save dir for exported model url :param model_version: type int best :return:no return """ with tf.get_default_graph().as_default(): # prediction_signature tensor_info_input_0 = tf.saved_model.utils.build_tensor_info(model.input[0]) tensor_info_input_1 = tf.saved_model.utils.build_tensor_info(model.input[1]) tensor_info_output = tf.saved_model.utils.build_tensor_info(model.output) print(model.input) print(model.output.shape, '**', tensor_info_output) prediction_signature = ( tf.saved_model.signature_def_utils.build_signature_def( inputs ={'input_0': tensor_info_input_0,'input_1': tensor_info_input_1}, # Tensorflow.TensorInfo outputs={'result': tensor_info_output}, #method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME) method_name= "tensorflow/serving/predict") ) print('step1 => prediction_signature created successfully') # set-up a builder os.mkdir(export_model_dir) export_path_base = export_model_dir export_path = os.path.join( tf.compat.as_bytes(export_path_base), tf.compat.as_bytes(str(model_version))) builder = tf.saved_model.builder.SavedModelBuilder(export_path) builder.add_meta_graph_and_variables( # tags:SERVING,TRAINING,EVAL,GPU,TPU sess=K.get_session(), tags=[tf.saved_model.tag_constants.SERVING], signature_def_map={ 'predict':prediction_signature, tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: prediction_signature, }, ) print('step2 => Export path(%s) ready to export trained model' % export_path, '\n starting to export model...') #builder.save(as_text=True) builder.save() print('Done exporting!') export_model(model,"bert",1)

上述过程需要注意的一个人地方是模型API输入和输出的定义: 这个部分要按照模型的输入输出定义好。由于我的bert模型 定义如下:

输入token_id和segment_id两部分输入, 输出是dense层的输出 所以我的API定义过程如下 ###输入tensor tensor_info_input_0 = tf.saved_model.utils.build_tensor_info(model.input[0]) tensor_info_input_1 = tf.saved_model.utils.build_tensor_info(model.input[1]) ###输出tensor tensor_info_output = tf.saved_model.utils.build_tensor_info(model.output) ### 定义api prediction_signature = ( tf.saved_model.signature_def_utils.build_signature_def( inputs ={'input_0': tensor_info_input_0,'input_1': tensor_info_input_1}, # Tensorflow.TensorInfo outputs={'result': tensor_info_output}, #method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME) method_name= "tensorflow/serving/predict") )

下图是脚本执行输出:

res

采用上述代码将model 保持成Saved_model 格式后,模型文件结构如下图所示:包含版本,模型pd文件,参数的文件夹。

Saved_model TensorFlow Serving 之使用Docker 部署Saved_model

使用docker部署模型的好处在于,避免了与繁琐的环境配置打交道。使用docker,不需要手动安装Python,更不需要安装numpy、tensorflow各种包。google 已经帮你制作好了tensorflow/serving 镜像,GPU版和CPU版都有,你只要要使用docker pull 命令将镜像拉取到本地就可以了

run -p 8501:8501 --mount type=bind,source=/opt/developer/wp/learn/bert,target=/models/bert -e MODEL_NAME=bert --name bert -t tensorflow/serving

使用上面的docker命令启动TF Server : (1)-p 8501:8501是端口映射,是将容器的8501端口映射到宿主机的8501端口,后面预测的时候使用该端口; (2)-e MODEL_NAME=bert 设置模型名称; (3)--mount type=bind,source=/opt/developer/wp/learn/bert, target=/models/bert 是将宿主机的路径/opt/developer/wp/learn/bert 挂载到容器的/models/bert 下。 /opt/developer/wp/learn/bert是通过上述py脚本生成的Saved_model的路径。容器内部会根据绑定的路径读取模型文件;

使用下方命令行查看服务状态

curl http://localhost:8501/v1/models/bert image.png 请求服务

加下来使用request库尝试请求一下我们的bert服务。 这里需要注意的是数据预处理的方式要和你做训练时的方式一样。

sent = "来玩英雄联盟" tokenid_train = tokenizer.encode(sent,max_len=200)[0] sen_id_train = tokenizer.encode(sent,max_len=200)[1] import requests SERVER_URL = "http://192.168.77.40:8501/v1/models/bert:predict" predict_request='{"signature_name": "predict", "instances":[{"input_0":%s,"input_1":%s}] }' %(tokenid_train,sen_id_train) response = requests.post(SERVER_URL, data=predict_request) response.content

返回结果:{ "predictions": [[8.70507502e-05, 0.999913]]} 当然你也可以使用gRPC 的API在8500端口访问你的服务。

结语

今天笔者只是简单介绍了一下,如何将模型转换为生产环境能用与部署的格式,以及使用docker部署模型的方式,其实模型训练出来了,达到了很好的效果,接下来让更多的人能够方便的使用到它们也是我们算法工程师所期望的事情,所以,模型的部署还是很有意义的一件事情。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3